{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6a22b270",
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install langchain==0.0.142 openai==0.27.4 beautifulsoup4==4.12.2 chromadb==0.3.21 GitPython==3.1.31"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "8513347e",
   "metadata": {},
   "outputs": [],
   "source": [
    "import requests\n",
    "from bs4 import BeautifulSoup\n",
    "import pandas as pd\n",
    "\n",
    "from langchain.document_loaders import (\n",
    "    GitLoader,\n",
    "    YoutubeLoader,\n",
    "    DataFrameLoader\n",
    ")\n",
    "from langchain.text_splitter import CharacterTextSplitter\n",
    "from langchain.schema import Document\n",
    "from langchain.embeddings import OpenAIEmbeddings\n",
    "from langchain.chat_models import ChatOpenAI\n",
    "from langchain.vectorstores import Chroma\n",
    "from langchain.chains import RetrievalQA\n",
    "from langchain.prompts import PromptTemplate\n",
    "from langchain.agents import initialize_agent, Tool"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dfeb22bf",
   "metadata": {},
   "source": [
    "# Implementing a sales & support agent with LangChain\n",
    "## Learn how to develop a chatbot that can answer questions based on the information provided in your company's documentation\n",
    "\n",
    "Recently, I have been fascinated by the power of ChatGPT and its ability to construct various types of chatbots. I have tried and written about multiple approaches to implementing a chatbot that can access external information to improve its answers. I joined a few Discord channels during my chatbot coding sessions, hoping to get some help as the libraries are relatively new, and not much documentation is available yet. To my amazement, I found custom bots that could answer most of the questions for the given library.\n",
    "\n",
    "The idea is to provide the chatbot the ability to dig through various resources like company documentation, code, or other content in order to allow it to answer company support questions. Since I already have some experience with chatbots, I decided to test how hard it is to implement a custom bot with access to the company's resources.\n",
    "In this blog post, I will walk you through how I used OpenAI's models to implement a sales & support agent with in the LangChain library that can be used to answer information about applications with a graph database Neo4j. The agent can also help you debug or produce any Cypher statement you are struggling with. Such an agent could then be deployed to serve users on Discord or other platforms.\n",
    "\n",
    "We will be using the LangChain library to implement the support bot. The library is easy to use and provides an excellent integration of LLM prompts and Python code, allowing us to develop chatbots in only a few minutes. In addition, the library supports a range of LLMs, text embedding models, and vector databases, along with utility functions that help us load and embed frequent types of files we might come across, like text, PowerPoint, images, HTML, PDF, and more.\n",
    "\n",
    "## LangChain document loaders\n",
    "First, we must preprocess the company's resources and store them in a vector database. Luckily, LangChain can help us load external data, calculate text embeddings, and store the documents in a vector database of our choice.\n",
    "First, we have to load the text into documents. LangChain offers a variety of helper functions that can take various formats and types of data and produce a document output. The helper functions are called Document loaders.\n",
    "\n",
    "Neo4j has a lot of its documentation available in GitHub repositories. Conveniently, LangChain provides a document loader that takes a repository URL as input and produces a document for each file in the repository. Additionally, we can use the filter function to ignore files during the loading process if needed.\n",
    "\n",
    "We will begin by loading the AsciiDoc files from the Neo4j's knowledge base repository."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "ee481063",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "309\n"
     ]
    }
   ],
   "source": [
    "# Knowledge base\n",
    "kb_loader = GitLoader(\n",
    "    clone_url=\"https://github.com/neo4j-documentation/knowledge-base\",\n",
    "    repo_path=\"./repos/kb/\",\n",
    "    branch=\"master\",\n",
    "    file_filter=lambda file_path: file_path.endswith(\".adoc\")\n",
    "    and \"articles\" in file_path,\n",
    ")\n",
    "kb_data = kb_loader.load()\n",
    "print(len(kb_data))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e5383390",
   "metadata": {},
   "source": [
    "Wasn't that easy as a pie? The GitLoader function clones the repository and load relevant files as documents. In this example, we specified that the file must end with .adoc suffix and be a part of the articles folder. In total, 309 articles were loaded. We also have to be mindful of the size of the documents. For example, GPT-3.5-turbo has a token limit of 4000, while GPT-4 allows 8000 tokens in a single request. While number of words is not exactly identical to the number of tokens, it is still a good estimator. \n",
    "\n",
    "Next, we will load the documentation of the Graph Data Science repository. Here, we will use a text splitter to make sure none of the documents exceed 2000 words. Again, I know that number of words is not equal to the number of tokens, but it is a good approximation. Defining the threshold number of tokens can significantly affect how the database is found and retrieved."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "f45bdb07",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "771\n"
     ]
    }
   ],
   "source": [
    "# Define text chunk strategy\n",
    "splitter = CharacterTextSplitter(\n",
    "  chunk_size=2000, \n",
    "  chunk_overlap=50,\n",
    "  separator=\" \"\n",
    ")\n",
    "# GDS guides\n",
    "gds_loader = GitLoader(\n",
    "    clone_url=\"https://github.com/neo4j/graph-data-science\",\n",
    "    repo_path=\"./repos/gds/\",\n",
    "    branch=\"master\",\n",
    "    file_filter=lambda file_path: file_path.endswith(\".adoc\") \n",
    "    and \"pages\" in file_path,\n",
    ")\n",
    "gds_data = gds_loader.load()\n",
    "# Split documents into chunks\n",
    "gds_data_split = splitter.split_documents(gds_data)\n",
    "print(len(gds_data_split))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "70afdb4b",
   "metadata": {},
   "source": [
    "We could load other Neo4j repositories that contain documentation. However, the idea is to show various data loading methods and not explore all of Neo4j's repositories containing documentation. Therefore, we will move on and look at how we can load documents from a Pandas Dataframe.\n",
    "\n",
    "For example, say that we want to load a YouTube video as a document source for our chatbot. Neo4j has its own YouTube channel and, even I appear in a video or two. Two years ago I presented how to implement an information extraction pipeline.\n",
    "With LangChain, we can use the captions of the video and load it as documents with only three lines of code."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "bbdd3ed3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10\n"
     ]
    }
   ],
   "source": [
    "# Youtube\n",
    "yt_loader = YoutubeLoader(\"1sRgsEKlUr0\")\n",
    "yt_data = yt_loader.load()\n",
    "yt_data_split = splitter.split_documents(yt_data)\n",
    "print(len(yt_data_split))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ce9220b7",
   "metadata": {},
   "source": [
    "It couldn't get any easier than this. Next, we will look at loading documents from a Pandas dataframe. A month ago, I retrieved information from Neo4j medium publication for a separate blog post. Since we want to bring external information about Neo4j to the bot, we can also use the content of the medium articles."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "c81c834c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2244\n"
     ]
    }
   ],
   "source": [
    "# Medium\n",
    "article_url = \"https://raw.githubusercontent.com/tomasonjo/blog-datasets/main/medium/neo4j_articles.csv\"\n",
    "medium = pd.read_csv(article_url, sep=\";\")\n",
    "medium[\"source\"] = medium[\"url\"]\n",
    "medium_loader = DataFrameLoader(\n",
    "    medium[[\"text\", \"source\"]], \n",
    "    page_content_column=\"text\")\n",
    "medium_data = medium_loader.load()\n",
    "medium_data_split = splitter.split_documents(medium_data)\n",
    "print(len(medium_data_split))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ae0ac744",
   "metadata": {},
   "source": [
    "Here, we used Pandas to load a CSV file from GitHub, renamed one column, and used the DataFrameLoaderfunction to load the articles as documents. Since medium posts could exceed 4000 tokens, we used the text splitter to split the articles into multiple chunks.\n",
    "\n",
    "The last source we will use is the Stack Overflow API. Stack Overflow is a web platform where users help others solve coding problems. Their API does not require any authorization. Therefore, we can use the API to retrieve questions with accepted answers that are tagged with the Neo4j tag."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "d1a0e03e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "775\n"
     ]
    }
   ],
   "source": [
    "# Stackoverflow\n",
    "so_data = []\n",
    "for i in range(1, 20):\n",
    "    # Define the Stack Overflow API endpoint and parameters\n",
    "    api_url = \"https://api.stackexchange.com/2.3/questions\"\n",
    "    params = {\n",
    "        \"order\": \"desc\",\n",
    "        \"sort\": \"creation\",\n",
    "        \"filter\": \"!-MBrU_IzpJ5H-AG6Bbzy.X-BYQe(2v-.J\",\n",
    "        \"tagged\": \"neo4j\",\n",
    "        \"site\": \"stackoverflow\",\n",
    "        \"pagesize\": 100,\n",
    "        \"page\": i,\n",
    "    }\n",
    "    # Send GET request to Stack Overflow API\n",
    "    response = requests.get(api_url, params=params)\n",
    "    data = response.json()\n",
    "    # Retrieve the resolved questions\n",
    "    resolved_questions = [\n",
    "        question\n",
    "        for question in data[\"items\"]\n",
    "        if question[\"is_answered\"] and question.get(\"accepted_answer_id\")\n",
    "    ]\n",
    "\n",
    "    # Print the resolved questions\n",
    "    for question in resolved_questions:\n",
    "        text = (\n",
    "            \"Title:\",\n",
    "            question[\"title\"] + \"\\n\" + \"Question:\",\n",
    "            BeautifulSoup(question[\"body\"]).get_text()\n",
    "            + \"\\n\"\n",
    "            + BeautifulSoup(\n",
    "                [x[\"body\"] for x in question[\"answers\"] if x[\"is_accepted\"]][0]\n",
    "            ).get_text(),\n",
    "        )\n",
    "        source = question[\"link\"]\n",
    "        so_data.append(Document(page_content=str(text), metadata={\"source\": source}))\n",
    "print(len(so_data))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c51352c2",
   "metadata": {},
   "source": [
    "Each approved answer and the original question are used to construct a single document. Since most Stack overflow questions and answers do not exceed 4000 tokens, we skipped the text-splitting step.\n",
    "Now that we have loaded the documentation resources as documents, we can move on to the next step.\n",
    "# Storing documents in a vector database\n",
    "A chatbot finds relevant information by comparing the vector embedding of questions with document embeddings. A text embedding is a machine-readable representation of text in the form of a vector or, more plainly, a list of floats. In this example, we will use the ada-002 model provided by OpenAI to embed documents.\n",
    "The whole idea behind vector databases is the ability to store vectors and provide fast similarity searches. The vectors are usually compared using cosine similarity. LangChain includes integration with a variety of vector databases. To keep things simple, we will use the Chroma vector database, which can be used as a local in-memory. For a more serious chatbot application, we want to use a persistent database that doesn't lose data once the script or notebook is closed.\n",
    "\n",
    "We will create two collections of documents. The first will be more sales and marketing oriented, containing documents from Medium and YouTube. The second collection focuses more on support use cases and consists of documentation and Stack Overflow documents."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "8955bd54",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using embedded DuckDB without persistence: data will be transient\n",
      "Using embedded DuckDB without persistence: data will be transient\n"
     ]
    }
   ],
   "source": [
    "# Define embedding model\n",
    "OPENAI_API_KEY = \"OPENAI_API_KEY\"\n",
    "embeddings = OpenAIEmbeddings(openai_api_key=OPENAI_API_KEY)\n",
    "\n",
    "sales_data = medium_data_split + yt_data_split\n",
    "sales_store = Chroma.from_documents(\n",
    "    sales_data, embeddings, collection_name=\"sales\"\n",
    ")\n",
    "\n",
    "support_data = kb_data + gds_data_split + so_data\n",
    "support_store = Chroma.from_documents(\n",
    "    support_data, embeddings, collection_name=\"support\"\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7176f1bf",
   "metadata": {},
   "source": [
    "This script runs each document through OpenAI's text embedding API and inserts the resulting embedding along with text in the Chroma database. The process of text embedding costs 0.80$, which is a reasonable price.\n",
    "## Question answering using external context\n",
    "\n",
    "The last thing to do is to implement two separate question-answering flow. The first will handle the sales & marketing requests, while the other will handle support. The LangChain library uses LLMs for reasoning and providing answers to the user. Therefore, we start by defining the LLM. Here, we will be using the GPT-3.5-turbo model from OpenAI."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "13ac3cea",
   "metadata": {},
   "outputs": [],
   "source": [
    "llm = ChatOpenAI(\n",
    "    model_name=\"gpt-3.5-turbo\",\n",
    "    temperature=0,\n",
    "    openai_api_key=OPENAI_API_KEY,\n",
    "    max_tokens=512,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ce3c7e9a",
   "metadata": {},
   "source": [
    "Implementing a question-answering flow is about as easy as it gets with LangChain. We only need to provide the LLM to be used along with the retriever that is used to fetch relevant documents. Additionally, we have the option to customize the LLM prompt used to answer questions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "8b8b1bb2",
   "metadata": {},
   "outputs": [],
   "source": [
    "sales_template = \"\"\"As a Neo4j marketing bot, your goal is to provide accurate and helpful information about Neo4j,\n",
    "a powerful graph database used for building various applications.\n",
    "You should answer user inquiries based on the context provided and avoid making up answers.\n",
    "If you don't know the answer, simply state that you don't know.\n",
    "Remember to provide relevant information about Neo4j's features, benefits,\n",
    "and use cases to assist the user in understanding its value for application development.\n",
    "\n",
    "{context}\n",
    "\n",
    "Question: {question}\"\"\"\n",
    "SALES_PROMPT = PromptTemplate(\n",
    "    template=sales_template, input_variables=[\"context\", \"question\"]\n",
    ")\n",
    "sales_qa = RetrievalQA.from_chain_type(\n",
    "    llm=llm,\n",
    "    chain_type=\"stuff\",\n",
    "    retriever=sales_store.as_retriever(),\n",
    "    chain_type_kwargs={\"prompt\": SALES_PROMPT},\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c08b7e9c",
   "metadata": {},
   "source": [
    "The most important part of the sales prompt is to prohibit the LLM from basing its responses without relying on official company resources. Remember, LLMs can act very assertively while providing invalid information. However, we would like to avoid that scenario and avoid getting into problems where the bot promised or sold non-existing features. We can test the sales question answering flow by asking the following question:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "8c38f909",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Neo4j is a powerful graph database that offers several benefits for application development. Some of the main benefits include:\n",
      "\n",
      "1. High Performance: Neo4j is designed to handle complex queries and large datasets, making it ideal for applications that require high performance.\n",
      "\n",
      "2. Flexible Data Model: Neo4j's flexible data model allows developers to easily store and query complex relationships between data points, making it ideal for applications that require complex data modeling.\n",
      "\n",
      "3. Scalability: Neo4j is highly scalable, allowing developers to easily add new nodes and relationships as their application grows.\n",
      "\n",
      "4. Easy to Use: Neo4j's query language, Cypher, is easy to learn and use, making it accessible to developers of all skill levels.\n",
      "\n",
      "5. Real-Time Insights: Neo4j's ability to quickly analyze complex relationships between data points allows developers to gain real-time insights into their data, making it ideal for applications that require real-time data analysis.\n",
      "\n",
      "Overall, Neo4j is a powerful tool for building complex applications that require flexible data modeling, high performance, and real-time insights.\n"
     ]
    }
   ],
   "source": [
    "print(sales_qa.run(\"What are the main benefits of using Neo4j?\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dec9aa44",
   "metadata": {},
   "source": [
    "The response to the question seems relevant and accurate. Remember, the information to construct this response came from Medium articles.\n",
    "\n",
    "Next, we will implement the support question-answering flow. Here, we will allow the LLM model to use its knowledge of Cypher and Neo4j to help solve the user's problem if the context doesn't provide enough information."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "e10cdf4a",
   "metadata": {},
   "outputs": [],
   "source": [
    "support_template = \"\"\"\n",
    "As a Neo4j Customer Support bot, you are here to assist with any issues \n",
    "a user might be facing with their graph database implementation and Cypher statements.\n",
    "Please provide as much detail as possible about the problem, how to solve it, and steps a user should take to fix it.\n",
    "If the provided context doesn't provide enough information, you are allowed to use your knowledge and experience to offer you the best possible assistance.\n",
    "\n",
    "{context}\n",
    "\n",
    "Question: {question}\"\"\"\n",
    "\n",
    "SUPPORT_PROMPT = PromptTemplate(\n",
    "    template=support_template, input_variables=[\"context\", \"question\"]\n",
    ")\n",
    "\n",
    "support_qa = RetrievalQA.from_chain_type(\n",
    "    llm=llm,\n",
    "    chain_type=\"stuff\",\n",
    "    retriever=support_store.as_retriever(),\n",
    "    chain_type_kwargs={\"prompt\": SUPPORT_PROMPT},\n",
    ")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "22b84a31",
   "metadata": {},
   "source": [
    "And again, we can test the support question-answering abilities. I took a random question from Neo4j's discord server."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "22bfcb3a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "To install the GDS plugin on a Neo4j server running on a VM, you can follow the manual installation steps provided in the Neo4j documentation. \n",
      "\n",
      "1. Download the `neo4j-graph-data-science-[version].zip` file from the Neo4j Download Center.\n",
      "2. Unzip the archive and move the `neo4j-graph-data-science-[version].jar` file into the `$NEO4J_HOME/plugins` directory.\n",
      "3. Add the following to your `$NEO4J_HOME/conf/neo4j.conf` file:\n",
      "```\n",
      "dbms.security.procedures.unrestricted=gds.*\n",
      "```\n",
      "4. Check if the procedure allowlist is enabled in the `$NEO4J_HOME/conf/neo4j.conf` file and add the GDS library if necessary:\n",
      "```\n",
      "dbms.security.procedures.allowlist=gds.*\n",
      "```\n",
      "5. Restart Neo4j.\n",
      "\n",
      "After following these steps, you should be able to use the GDS plugin on your Neo4j server running on the VM. You can verify the installation by running the `gds.version()` function in Neo4j Browser.\n"
     ]
    }
   ],
   "source": [
    "print(support_qa.run(\"\"\"\n",
    "I am having my graph in a VM and i want to use GDS Plugins in my graph.\n",
    "I didn't see any proper documentation to install it in my server where it was working only on locally.\n",
    "Anyone clear me this to install the GDS Plugin in the VM ?\n",
    "\"\"\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6402502d",
   "metadata": {},
   "source": [
    "The response is quite to the point. Remember, we retrieved the Graph Data Science documentation and are using it as context to form the chatbot questions.\n",
    "## Agent implementation\n",
    "We now have two separate instructions and stores for sales and support responses. If we had to put a human in the loop to distinguish between the two, the whole point of the chatbot would be lost. Luckily, we can use a LangChain agent to decide which tool to use based on the user input. First, we need to define the available tools of an agent along with instructions on when and how to use them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "1179d1ef",
   "metadata": {},
   "outputs": [],
   "source": [
    "# the zero-shot-react-description agent will use the \"description\" string to select tools\n",
    "tools = [\n",
    "    Tool(\n",
    "        name = \"sales\",\n",
    "        func=sales_qa.run,\n",
    "        description=\"\"\"useful for when a user is interested in various Neo4j information, \n",
    "                       use-cases, or applications. A user is not asking for any debugging, but is only\n",
    "                       interested in general advice for integrating and using Neo4j.\n",
    "                       Input should be a fully formed question.\"\"\"\n",
    "    ),\n",
    "    Tool(\n",
    "        name = \"support\",\n",
    "        func=support_qa.run,\n",
    "        description=\"\"\"useful for when when a user asks to optimize or debug a Cypher statement or needs\n",
    "                       specific instructions how to accomplish a specified task. \n",
    "                       Input should be a fully formed question.\"\"\"\n",
    "    ),\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aff7694f",
   "metadata": {},
   "source": [
    "The description of a tool is used by an agent to identify when and how to use a tool. For example, the support tool should be used to optimize or debug a Cypher statement and the input to the tool should be a fully formed question.\n",
    "\n",
    "The last thing we need to do is to initialize the agent."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4b335506",
   "metadata": {},
   "outputs": [],
   "source": [
    "agent = initialize_agent(tools, llm, agent=\"zero-shot-react-description\", verbose=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ec74be42",
   "metadata": {},
   "source": [
    "Now we can go ahead and test the agent on a couple of questions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "52863aac",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mThis question is asking for general advice on using Neo4j with GPT-4, so the best tool to use would be sales.\n",
      "Action: sales\n",
      "Action Input: \"What are some use-cases for integrating Neo4j with GPT-4?\"\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3mThere are several use cases for integrating Neo4j with GPT-4. One example is using GPT-4 as a domain expert to help extract knowledge from a video transcript and then storing that information in a Neo4j knowledge graph. Another example is using GPT-4 for relationship extraction to accelerate knowledge graph construction in Neo4j. Additionally, GPT-4 can be used in conjunction with Neo4j to develop a knowledge graph-based chatbot that provides answers based on data stored in the knowledge graph. Overall, integrating Neo4j with GPT-4 can enhance the capabilities of both technologies and enable more powerful and accurate applications.\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3mI now know the final answer\n",
      "Final Answer: There are several use cases for integrating Neo4j with GPT-4, including using GPT-4 as a domain expert to extract knowledge from a video transcript and storing it in a Neo4j knowledge graph, using GPT-4 for relationship extraction to accelerate knowledge graph construction, and developing a knowledge graph-based chatbot that provides answers based on data stored in the knowledge graph.\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'There are several use cases for integrating Neo4j with GPT-4, including using GPT-4 as a domain expert to extract knowledge from a video transcript and storing it in a Neo4j knowledge graph, using GPT-4 for relationship extraction to accelerate knowledge graph construction, and developing a knowledge graph-based chatbot that provides answers based on data stored in the knowledge graph.'"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent.run(\"\"\"What are some GPT-4 applications with Neo4j?\"\"\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "ece1136f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mThis sounds like a technical question that requires specific instructions or optimization. \n",
      "\n",
      "Action: support\n",
      "Action Input: Ask the user for more information about their specific use case and what they are trying to accomplish with the weighted shortest path query. Ask if they have tried any alternative methods or libraries. \u001b[0m\n",
      "Observation: \u001b[33;1m\u001b[1;3mAs a Neo4j Customer Support bot, I would like to ask for more information about your specific use case and what you are trying to accomplish with the weighted shortest path query. Have you tried any alternative methods or libraries? This will help me provide you with the best possible assistance.\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3mBased on the user's response, I may need to provide specific instructions or suggest alternative methods.\n",
      "\n",
      "Action: support\n",
      "Action Input: Based on the user's response, provide specific instructions or suggest alternative methods for executing a weighted shortest path query in the Neo4j Community edition.\u001b[0m\n",
      "Observation: \u001b[33;1m\u001b[1;3mTo execute a weighted shortest path query in the Neo4j Community edition, you can use the APOC library. Here's an example query:\n",
      "\n",
      "```\n",
      "MATCH (start:Node {name: \"Start\"}), (end:Node {name: \"End\"})\n",
      "CALL apoc.algo.dijkstraWithDefaultWeight(start, end, \"CONNECTED_TO\", \"weight\")\n",
      "YIELD path, weight\n",
      "RETURN path, weight\n",
      "```\n",
      "\n",
      "In this query, \"CONNECTED_TO\" is the name of the relationship type, and \"weight\" is the name of the property on the relationship that contains the weight. You can replace these values with the appropriate names for your graph.\n",
      "\n",
      "If you don't want to use the APOC library, you can also implement Dijkstra's algorithm manually in Cypher. Here's an example query:\n",
      "\n",
      "```\n",
      "MATCH (start:Node {name: \"Start\"}), (end:Node {name: \"End\"})\n",
      "WITH start, end\n",
      "MATCH path = (start)-[:CONNECTED_TO*]->(end)\n",
      "WHERE all(rel in relationships(path) WHERE rel.weight IS NOT NULL)\n",
      "WITH path, reduce(weight = 0, rel in relationships(path) | weight + rel.weight) AS totalWeight\n",
      "RETURN path, totalWeight\n",
      "ORDER BY totalWeight ASC\n",
      "LIMIT 1\n",
      "```\n",
      "\n",
      "In this query, \"CONNECTED_TO\" is the name of the relationship type, and \"weight\" is the name of the property on the relationship that contains the weight. You can replace these values with the appropriate names for your graph. This query finds the shortest path between the start and end nodes based on the sum of the weights of the relationships in the path.\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3mI now know the final answer.\n",
      "\n",
      "Final Answer: To execute a weighted shortest path query in the Neo4j Community edition, you can use the APOC library or implement Dijkstra's algorithm manually in Cypher. The APOC library provides a built-in function for Dijkstra's algorithm, while the manual implementation requires a Cypher query that finds the shortest path based on the sum of the weights of the relationships in the path.\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "\"To execute a weighted shortest path query in the Neo4j Community edition, you can use the APOC library or implement Dijkstra's algorithm manually in Cypher. The APOC library provides a built-in function for Dijkstra's algorithm, while the manual implementation requires a Cypher query that finds the shortest path based on the sum of the weights of the relationships in the path.\""
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent.run(\"\"\"\n",
    "Hello everyone, is there a way to execute a weighted shortest path query in the Neo4j Community edition?\n",
    "All I have found on the internet was gds library or the algo library, which are both unavailable the community version.\n",
    "\"\"\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "627daef9",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "scrape",
   "language": "python",
   "name": "scrape"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.15"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
